﻿#include "matrix.h"
#include "base.h"
#include "types.h"
#include "Constant.h"
#include <sstream>
matrix::matrix(void)
{
	LoadIdentity();
}
matrix::matrix(const matrix &other)
{
	memcpy((void*)m_vars, other.m_vars, 16 * sizeof(float));
}
matrix::~matrix(void)
{
}
void matrix::Zero()
{
	for(int i = 0; i < 16; i++)
	{
		m_vars[i] = 0;
	}
}
void matrix::LoadIdentity()
{
	for(int i = 0; i < 16; i++)
	{
		if((i % 5) == 0)
		{
			m_vars[i] = 1;
		}else{
			m_vars[i] = 0;
		}
	}
}
matrix matrix::Transpose() const
{
	matrix _tmp;
	for(uint i = 0; i < 4; i++)
	{
		for(uint j = 0; j < 4; j++)
		{
			_tmp.m_vars[MAT_INDEX(i,j)] = m_vars[MAT_INDEX(j,i)];
		}
	}
	return _tmp;
}
matrix matrix::operator=(const matrix &other)
{
	memcpy((void*)m_vars, other.m_vars, 16 * sizeof(float));
	return (*this);
}
matrix matrix::operator+(const matrix &other) const
{
	matrix _tmp;
	for(uint i = 0; i < 4; i++)
	{
		for(uint j = 0; j < 4; j++)
		{
			_tmp.m_vars[MAT_INDEX(i,j)] = other.m_vars[MAT_INDEX(i,j)] - m_vars[MAT_INDEX(i,j)];
		}
	}
	return _tmp;
}
matrix matrix::operator-(const matrix &other) const
{
	matrix _tmp;
	for(uint i = 0; i < 4; i++)
	{
		for(uint j = 0; j < 4; j++)
		{
			_tmp.m_vars[MAT_INDEX(i,j)] = other.m_vars[MAT_INDEX(i,j)] + m_vars[MAT_INDEX(i,j)];
		}
	}
	return _tmp;
}
vector4 matrix::operator*(const vector4 &other) const
{
	vector4 _tmp(0,0,0,0);

	_tmp.x += other.x * m_vars[MAT_INDEX(0,0)];
	_tmp.x += other.y * m_vars[MAT_INDEX(1,0)];
	_tmp.x += other.z * m_vars[MAT_INDEX(2,0)];
	_tmp.x += other.w * m_vars[MAT_INDEX(3,0)];

	_tmp.y += other.x * m_vars[MAT_INDEX(0,1)];
	_tmp.y += other.y * m_vars[MAT_INDEX(1,1)];
	_tmp.y += other.z * m_vars[MAT_INDEX(2,1)];
	_tmp.y += other.w * m_vars[MAT_INDEX(3,1)];

	_tmp.z += other.x * m_vars[MAT_INDEX(0,2)];
	_tmp.z += other.y * m_vars[MAT_INDEX(1,2)];
	_tmp.z += other.z * m_vars[MAT_INDEX(2,2)];
	_tmp.z += other.w * m_vars[MAT_INDEX(3,2)];

	_tmp.w += other.x * m_vars[MAT_INDEX(0,3)];
	_tmp.w += other.y * m_vars[MAT_INDEX(1,3)];
	_tmp.w += other.z * m_vars[MAT_INDEX(2,3)];
	_tmp.w += other.w * m_vars[MAT_INDEX(3,3)];

	return _tmp;
}
matrix matrix::operator*(const matrix &other) const
{
	matrix _tmp;
	_tmp.Zero();
	for(uint i = 0; i < 4; i++)
	{
		for(uint j = 0; j < 4; j++)
		{
			for(uint s = 0; s < 4; s++)
			{
				_tmp.m_vars[MAT_INDEX(j,i)] += m_vars[MAT_INDEX(s,i)] * other.m_vars[MAT_INDEX(j,s)];
			}
		}
	}
	return _tmp;
}
float *matrix::DataPtr()
{
	return (float*)m_vars;
}
const float *matrix::DataPtr() const
{
	return (const float*)m_vars;
}
std::string matrix::ToString() const
{
	std::stringstream _tmp;
	for(int y = 0; y < 4; y++)
	{
		_tmp << "[";
		for(int x = 0; x < 4; x++)
		{
			_tmp << m_vars[MAT_INDEX(x,y)];
			if (x < 3)
			{
				_tmp << "\t";
			}
		}
		_tmp << "]";
		if(y < 3)
		{
			_tmp << "\n";
		}
	}
	return _tmp.str();
}
matrix matrix::Translation(const vector3 &position)
{
	matrix _tmp;
	_tmp.m_vars[MAT_INDEX(3,0)] = position.x;
	_tmp.m_vars[MAT_INDEX(3,1)] = position.y;
	_tmp.m_vars[MAT_INDEX(3,2)] = position.z;
	return _tmp;
}
matrix matrix::Perspective(double fovX, double fAspect, double zNear, double zFar)
{
	matrix _tmp;
	double fovY = fovX / fAspect;
	float f = 1.0f/tanf(fovY*2);
	
	//1st-column
	_tmp.m_vars[MAT_INDEX(0,0)] = -f/fAspect;
	//2nd-column
	_tmp.m_vars[MAT_INDEX(1,1)] = -f;
	//3th-column
	_tmp.m_vars[MAT_INDEX(2,2)] = -(zFar+zNear)/(zNear-zFar);
	_tmp.m_vars[MAT_INDEX(2,3)] = 1.0f;
	//4th-column
	_tmp.m_vars[MAT_INDEX(3,2)] = (2*zFar*zNear)/(zNear-zFar);
	_tmp.m_vars[MAT_INDEX(3,3)] = 0.0f;
	//_tmp.m_vars[MAT_INDEX(2,3)] = 0.0f;

	return _tmp;
}
matrix matrix::CenteredOrthogonal(double width, double heigth, double zNear, double zFar)
{
	matrix _tmp;
	_tmp.m_vars[MAT_INDEX(0,0)] = 2.0f/width;
	_tmp.m_vars[MAT_INDEX(1,1)] =- 2.0f/heigth;
	_tmp.m_vars[MAT_INDEX(2,2)] = 0.0f;
	_tmp.m_vars[MAT_INDEX(3,0)] = -1.0f;
	_tmp.m_vars[MAT_INDEX(3,1)] = 1.0f;
	_tmp.m_vars[MAT_INDEX(3,2)] = 0.0f;
	return _tmp;
}
matrix matrix::Orthogonal(double left, double right, double bottom, double top, double zNear, double zFar)
{
	matrix _tmp;
	_tmp.m_vars[MAT_INDEX(0,0)] = 2.0f/(right-left); //Scale X
	_tmp.m_vars[MAT_INDEX(1,1)] = 2.0f/(top-bottom); //Scale Y
	_tmp.m_vars[MAT_INDEX(2,2)] = 2.0f/(zFar-zNear); //Scale Z
	_tmp.m_vars[MAT_INDEX(3,0)] = -((right + left)/(right-left)); //Trans X
	_tmp.m_vars[MAT_INDEX(3,1)] = -((top + bottom)/(top-bottom)); //Trans Y
	_tmp.m_vars[MAT_INDEX(3,2)] = -((zFar + zNear)/(zFar-zNear)); //Trans Z
	return _tmp;
}
matrix matrix::Scale(const vector3 &scale)
{
	matrix _tmp;
	_tmp.m_vars[MAT_INDEX(0,0)] = scale.x;
	_tmp.m_vars[MAT_INDEX(1,1)] = scale.y;
	_tmp.m_vars[MAT_INDEX(2,2)] = scale.z;
	return _tmp;
}
matrix matrix::Rotation(const vector3 &axis, const float &r)
{
	float cosR = cosf(r*DEG_TO_RAD);
	float sinR = sinf(r*DEG_TO_RAD);
	float invCosR = 1 - cosR;
	matrix _tmp;

	_tmp.m_vars[MAT_INDEX(0,0)] = axis.x*axis.x*invCosR + cosR;
	_tmp.m_vars[MAT_INDEX(0,1)] = axis.x*axis.y*invCosR + axis.z*cosR;
	_tmp.m_vars[MAT_INDEX(0,2)] = axis.x*axis.z*invCosR - axis.y*cosR;

	_tmp.m_vars[MAT_INDEX(1,0)] = axis.x*axis.x*invCosR - axis.z*cosR;
	_tmp.m_vars[MAT_INDEX(1,1)] = axis.x*axis.y*invCosR + cosR;
	_tmp.m_vars[MAT_INDEX(1,2)] = axis.x*axis.z*invCosR + axis.x*cosR;

	_tmp.m_vars[MAT_INDEX(2,0)] = axis.x*axis.x*invCosR + axis.y*cosR;
	_tmp.m_vars[MAT_INDEX(2,1)] = axis.x*axis.y*invCosR - axis.x*cosR;
	_tmp.m_vars[MAT_INDEX(2,2)] = axis.x*axis.z*invCosR + cosR;
	return _tmp;
}
matrix matrix::Rotation(const vector3 &rotation)
{
	matrix _tmp;
	float cosP = cosf(rotation.x*DEG_TO_RAD);
	float sinP = sinf(rotation.x*DEG_TO_RAD);
	float cosY = cosf(rotation.y*DEG_TO_RAD);
	float sinY = sinf(rotation.y*DEG_TO_RAD);
	float cosR = cosf(rotation.z*DEG_TO_RAD);
	float sinR = sinf(rotation.z*DEG_TO_RAD);
	_tmp.m_vars[MAT_INDEX(0,0)] = cosY * cosR;
	_tmp.m_vars[MAT_INDEX(1,1)] = cosP*cosR + sinP*sinY*sinR;
	_tmp.m_vars[MAT_INDEX(2,2)] = cosP*cosY;

	_tmp.m_vars[MAT_INDEX(0,1)] = cosY * sinR;
	_tmp.m_vars[MAT_INDEX(1,0)] = -cosP*sinR + sinP*sinY*cosR;

	_tmp.m_vars[MAT_INDEX(0,2)] = -sinY;
	_tmp.m_vars[MAT_INDEX(2,0)] = sinP*sinR + cosP*cosY*cosR;

	_tmp.m_vars[MAT_INDEX(1,2)] = sinP*cosY;
	_tmp.m_vars[MAT_INDEX(2,1)] = -sinP*cosR + cosP*sinY*sinR;
	return _tmp;
}
matrix matrix::RotationX(const float &r)
{
	matrix _tmp;
	float cosR = cosf(r*DEG_TO_RAD);
	float sinR = sinf(r*DEG_TO_RAD);
	_tmp.m_vars[MAT_INDEX(1,1)] = cosR;
	_tmp.m_vars[MAT_INDEX(2,1)] = sinR;
	_tmp.m_vars[MAT_INDEX(1,2)] = -sinR;
	_tmp.m_vars[MAT_INDEX(2,2)] = cosR;
	return _tmp;
}
matrix matrix::RotationY(const float &r)
{
	matrix _tmp;
	float cosR = cosf(r*DEG_TO_RAD);
	float sinR = sinf(r*DEG_TO_RAD);
	_tmp.m_vars[MAT_INDEX(0,0)] = cosR;
	_tmp.m_vars[MAT_INDEX(2,0)] = -sinR;
	_tmp.m_vars[MAT_INDEX(0,2)] = sinR;
	_tmp.m_vars[MAT_INDEX(2,2)] = cosR;
	return _tmp;
}
matrix matrix::RotationZ(const float &r)
{
	matrix _tmp;
	float cosR = cosf(r*DEG_TO_RAD);
	float sinR = sinf(r*DEG_TO_RAD);
	_tmp.m_vars[MAT_INDEX(0,0)] = cosR;
	_tmp.m_vars[MAT_INDEX(1,0)] = sinR;
	_tmp.m_vars[MAT_INDEX(0,1)] = -sinR;
	_tmp.m_vars[MAT_INDEX(1,1)] = cosR;
	return _tmp;
}